package com.carbon.core.util;

import com.carbon.core.enums.LinuxNiceEnum;
import org.apache.commons.lang3.StringUtils;

import java.io.*;
import java.util.concurrent.TimeUnit;

import static java.nio.charset.StandardCharsets.UTF_8;

/**
 * 执行外部命令
 *
 * @author shawenguan
 * @date 2019/06/12
 */
public class ProcessUtils {

    /**
     * 执行外部命令
     *
     * @param command 命令
     * @param timeout 超时时间
     */
    public static void executeCommand(String command, long timeout) {
        executeCommand(command, null, timeout, null);
    }

    /**
     * 执行外部命令
     *
     * @param command       命令
     * @param timeout       超时时间
     * @param linuxNiceEnum nice 命令优先级
     */
    public static void executeCommand(String command, long timeout, LinuxNiceEnum linuxNiceEnum) {
        executeCommand(command, null, timeout, linuxNiceEnum);
    }

    /**
     * 执行外部命令(指定执行目录)
     *
     * @param command   命令
     * @param directory 指定的目录
     * @param timeout   超时时间
     */
    public static void executeCommand(String command, String directory, long timeout) {
        executeCommand(command, directory, timeout, null);
    }

    /**
     * 执行外部命令(指定执行目录)
     *
     * @param command       命令
     * @param directory     指定的目录
     * @param timeout       超时时间
     * @param linuxNiceEnum nice 命令优先级
     */
    public static void executeCommand(String command, String directory, long timeout, LinuxNiceEnum linuxNiceEnum) {
        Runtime runtime = Runtime.getRuntime();
        Process process = null;
        BufferedReader reader = null;
        if (null != linuxNiceEnum) {
            command = linuxNiceEnum.commandPre() + command;
        }
        try {
            if (StringUtils.isEmpty(directory)) {
                process = runtime.exec(command);
            } else {
                process = runtime.exec(command, null, new File(directory));
            }

            reader = new BufferedReader(new InputStreamReader(process.getInputStream(), UTF_8));
            String line;
            StringBuilder sb = new StringBuilder();
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
            //运行 脚本默认并非是同步的，启用同步并且防止脚本内部出错死循环或者阻塞等，添加timeout
            boolean waitFor = process.waitFor(timeout, TimeUnit.MILLISECONDS);

            // 脚本输入即process的输出流未关闭，脚本会一直等待，主动关闭
            process.getOutputStream().close();

            // 脚本输出buffer满了，但是没有读取，导致waitFor()卡死 添加reader把它读出来(高清切图时会出现卡死的问题)
            char[] buf = new char[128];
            StringBuilder msgBuilder = null;
            if (process.exitValue() != 0 || !waitFor) {
                InputStream error = process.getErrorStream();
                BufferedReader br = new BufferedReader(new InputStreamReader(error, UTF_8));
                while (br.read(buf) != -1) {
                    if (msgBuilder == null) {
                        msgBuilder = new StringBuilder("");
                    }
                    msgBuilder.append(buf);
                }
            }
        } catch (IOException | InterruptedException e0) {
            e0.printStackTrace();
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 获取命令行内特定的值
     *
     * <p>如:
     * <blockquote><pre>
     * "total part count:   0   total part sum size:   0 total du size(byte):18325825"
     * ==> targetStr = "total du size(byte):" // 标识位;
     * ==> separator = " " // 空格;
     * <== result = "18325825"
     *  </pre></blockquote>
     *
     * @param command   命令
     * @param targetStr 目标字符串
     * @param separator 截止字符串
     */
    public static String getValueByTargetString(String command, String targetStr, String separator) throws Exception {
        String result = "";

        BufferedWriter out = null;
        BufferedReader in = null;
        BufferedReader err = null;

        try {
            Runtime runtime = Runtime.getRuntime();
            Process process = runtime.exec(command);

            // out 输出流，向子进程写入数据
            out = new BufferedWriter(new OutputStreamWriter(process.getOutputStream()));
            // in 输入流，获取子进程的输出
            in = new BufferedReader(new InputStreamReader(process.getInputStream()));
            // err 输入流，获取子进程打印的错误信息
            err = new BufferedReader(new InputStreamReader(process.getErrorStream()));

            StringBuilder totalLine = new StringBuilder();
            String line;
            while (((line = in.readLine()) != null)) {
                totalLine.append(line).append("\r\n");
            }
            String allLine = totalLine.toString();
            // 查询是否有我们要的数据
            boolean flag = allLine.contains(targetStr);
            if (flag) {
                allLine = allLine.substring(allLine.indexOf(targetStr) + targetStr.length());
                result = allLine.substring(0, allLine.indexOf(separator));
            }
            process.waitFor();
            return result;
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        } finally {
            if (out != null) {
                out.close();
            }
            if (in != null) {
                out.close();
            }
            if (err != null) {
                err.close();
            }
        }
    }
}